package com.loveprogrammer.javassist;

/**
 * @ClassName GenerateClazzMethod
 * @Description TODO
 * @Author admin
 * @Date 2023/11/17 16:28
 * @Version 1.0
 */

import javassist.*;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 1、CtField，属性字段的创建。这就像我们正常写代码一样，需要设定属性的；名称、类型以及是 public 的还是 private 的以及 static 和 final 等。都可以通过 Modifier.PRIVATE + Modifier.STATIC + Modifier.FINAL，通过组合来控制。同样这也适用于对方法类型的设置。同时需要在添加属性的地方，设置初始值。
 * 2、接下来是我们设置了一个求圆面积的方法，如果说在方法体中需要使用到入参类型。那么需要通过符号 $+数字，来获取入参。这个数字就是当前入参的位置。比如取第一个入参：$1，以此类推。
 * 3、之后是我们的多种入参类型，在这开始我们也提到了。如果是基本类型入参都可以使用 CtClass.doubleType，对象类型入参使用 pool.get(类.class.getName) 获取。
 * 4、最终同样我们会把使用字节码编译的 class 输出到工程目录下 ctClass.writeFile()。
 * 5、在Javassist中并不会给类型做拆箱和装箱操作，需要显式的处理。例如上面案例中，需要将 double 使用 Double.valueOf 进行转换。
 */
public class GenerateClazzMethodParams {

    /***
     * 在 Javassist 是不会进行类型的自动装箱和拆箱的，需要我们进行手动处理，否则生成类在执行会报类型错误。
     * @param args
     */
    public static void main(String[] args) throws CannotCompileException, NotFoundException, IOException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        ClassPool pool = ClassPool.getDefault();

        CtClass ctClass = pool.makeClass("com.loveprogrammer.javassist.MathUtil");

        // 属性字段
        CtField ctField = new CtField(CtClass.doubleType, "π", ctClass);
        ctField.setModifiers(Modifier.PRIVATE + Modifier.STATIC + Modifier.FINAL);
        ctClass.addField(ctField, "3.14");

        // 方法：求圆面积
        CtMethod calculateCircularArea = new CtMethod(CtClass.doubleType, "calculateCircularArea", new CtClass[]{CtClass.doubleType}, ctClass);
        calculateCircularArea.setModifiers(Modifier.PUBLIC);
        calculateCircularArea.setBody("{return π * $1 * $1;}");
        ctClass.addMethod(calculateCircularArea);

        // 方法；两数之和
        CtMethod sumOfTwoNumbers = new CtMethod(pool.get(Double.class.getName()), "sumOfTwoNumbers", new CtClass[]{CtClass.doubleType, CtClass.doubleType}, ctClass);
        sumOfTwoNumbers.setModifiers(Modifier.PUBLIC);
        sumOfTwoNumbers.setBody("{return Double.valueOf($1 + $2);}");
        ctClass.addMethod(sumOfTwoNumbers);
        // 输出类的内容
        ctClass.writeFile();

        // 测试调用
        Class clazz = ctClass.toClass();
        Object obj = clazz.newInstance();
        Method method_calculateCircularArea =
                clazz.getDeclaredMethod("calculateCircularArea", double.class);
        Object obj_01 = method_calculateCircularArea.invoke(obj, 1.23);
        System.out.println("圆面积：" + obj_01);
        Method method_sumOfTwoNumbers = clazz.getDeclaredMethod("sumOfTwoNumbers",
                double.class, double.class);
        Object obj_02 = method_sumOfTwoNumbers.invoke(obj, 1, 2);
        System.out.println("两数和：" + obj_02);

    }
}

